unit uAVRTimerForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, uBaseCalcForm, ExtCtrls, StdCtrls, Buttons;

type
  TfmAVRTimerCalc = class(TfmBaseCalcForm)
    laFreq: TLabel;
    edTicks: TLabeledEdit;
    laTicks: TLabel;
    edOvf: TLabeledEdit;
    laOvf: TLabel;
    edTime: TLabeledEdit;
    Bevel1: TBevel;
    cbResolution: TComboBox;
    cbPrescaler: TComboBox;
    Label1: TLabel;
    Label2: TLabel;
    cbUnitTime: TComboBox;
    cbFreq: TComboBox;
    laFreqCapt: TLabel;
    edTickTime: TLabeledEdit;
    rbTicks: TRadioButton;
    rbOvf: TRadioButton;
    rbTime: TRadioButton;
    imgLogo: TImage;
    edOvfFreq: TLabeledEdit;
    Bevel2: TBevel;
    laModeCapt: TLabel;
    cbMode: TComboBox;
    procedure DoFieldsChange(Sender: TObject);
    procedure cbFreqChange(Sender: TObject);
    procedure imgLogoClick(Sender: TObject);
    procedure cbModeChange(Sender: TObject);
  private
    { Private declarations }
    function GetTickTime: double;
    function GetOvfCount(t: single): single;
    procedure DoCalcTimer;
    procedure DoCalcCTC;
    procedure DoCalcFPWM;
    procedure DoCalcPPWM;
  protected
    function DoCheckFields: boolean; override;
    procedure FieldsChanged; override;
  public
    { Public declarations }
    class function CalcFriendlyName: string; override;
    class function RunKey: string; override;
    class procedure InvokeCalc; override;
    procedure AfterConstruction; override;
  end;

var
  fmAVRTimerCalc: TfmAVRTimerCalc;

implementation

{$R *.dfm}

uses uFormReg, uLocalization, uENoms, ShellApi;

const
  sfPresc: array[0..4] of single = (1.0, 8.0, 64.0, 256.0, 1024.0);
  sfTop: array[0..1] of single = (256.0, 65536.0);
  sfUnit: array[0..2] of single = (1e6, 1e3, 1.0);
  
var
  strUnitS: array[0..3] of String;
  strUnitF: array[0..2] of String;

// Format time interval
function formatValueS(Value: single): string;
begin
  //if (value<1.0e-6) then Result := Format('%.3f '+strUnitS[0], [Value*1.0e9])
  //else
  if (value<1.0e-3) then Result := Format('%.3f '+strUnitS[1], [Value*1.0e6])
  else
  if (value<1.0) then Result := Format('%.3f '+strUnitS[2], [Value*1.0e3])
  else Result := Format('%f '+strUnitS[3], [Value])
end;

// Format frequency
function formatValueF(Value: single): string;
begin
  if (value<1.0e3) then Result := Format('%f '+strUnitF[0], [Value])
  else
  if (value<1.0e6) then Result := Format('%.3f '+strUnitF[1], [Value*1.0e-3])
  else Result := Format('%.6f '+strUnitF[2], [Value*1.0e-6])
end;

{ TfmAVRTimerCalc }

procedure TfmAVRTimerCalc.AfterConstruction;
var i: integer;
begin
  inherited;
  LoadPictureFromRes(imgLogo.Picture, 'ATMEL');
  
  strUnitS[0] := getString('#global\nsec');
  strUnitS[1] := getString('#global\usec');
  strUnitS[2] := getString('#global\msec');
  strUnitS[3] := getString('#global\sec');
  strUnitF[0] := getString('#global\hz');
  strUnitF[1] := getString('#global\khz');
  strUnitF[2] := getString('#global\mhz');

  laFreqCapt.Caption := getLocaleString('corefreq');
  laModeCapt.Caption := getLocaleString('timode');
  laFreq.Caption := strUnitF[0];
  edTickTime.EditLabel.Caption := getLocaleString('ticktime');
  edOvfFreq.EditLabel.Caption := getLocaleString('ovffreq');
  edTicks.EditLabel.Caption := getLocaleString('ticks');
  //edOvf.EditLabel.Caption := getLocaleString('overflows');
  edTime.EditLabel.Caption := getLocaleString('time');
  Label1.Caption := getLocaleString('resolution');
  Label2.Caption := getLocaleString('prescaler');

  cbFreqChange(cbFreq);
  cbModechange(cbMode);

  for i := 0 to cbUnitTime.Items.Count-1 do
    cbUnitTime.Items[i] := getString('#global\'+cbUnitTime.Items[i]);
  cbUnitTime.ItemIndex := 1;
end;

class function TfmAVRTimerCalc.CalcFriendlyName: string;
begin
  Result := 'AVR Timer calculator';
end;

function TfmAVRTimerCalc.DoCheckFields: boolean;
begin
  Result := StrToIntDef(cbFreq.Text,0)>0;
end;

procedure TfmAVRTimerCalc.FieldsChanged;
begin
  case cbMode.ItemIndex of
    0: DoCalcTimer;
    1: DoCalcCTC;
    2: DoCalcFPWM;
    3: DoCalcPPWM;
  end;
end;

class procedure TfmAVRTimerCalc.InvokeCalc;
begin
  Application.CreateForm(TfmAVRTimerCalc, fmAVRTimerCalc);
  inherited;
end;

class function TfmAVRTimerCalc.RunKey: string;
begin
  Result := 'avrtimer';
end;

function TfmAVRTimerCalc.GetTickTime: double;
var tmp: integer;
begin
  Result := 0;
  tmp := StrToInt(cbFreq.Text);
  if (tmp<=0) then exit;
  Result := 1.0/(tmp/sfPresc[cbPrescaler.ItemIndex]);
end;

function TfmAVRTimerCalc.GetOvfCount(t: single): single;
begin
  Result := t/sfTop[cbResolution.ItemIndex];
end;

procedure TfmAVRTimerCalc.DoFieldsChange(Sender: TObject);
begin
  HighliteErrorField(edTicks, false);
  HighliteErrorField(edTime, false);
  HighliteErrorField(edOvf, false);

  if DoCheckFields then FieldsChanged;
end;

procedure TfmAVRTimerCalc.cbFreqChange(Sender: TObject);
begin
  inherited;
  edTickTime.Text := formatValueS(GetTickTime);
  edOvfFreq.Text := formatValueF(1.0/(GetTickTime*sfTop[cbResolution.ItemIndex]));
  
  Self.DoFieldsChange(Sender);
end;

procedure TfmAVRTimerCalc.imgLogoClick(Sender: TObject);
begin
  inherited;
  ShellExecute(0, 'open', 'http://www.atmel.com/products/microcontrollers/avr/default.aspx', nil, nil, SW_SHOW);
end;

procedure TfmAVRTimerCalc.cbModeChange(Sender: TObject);
begin
  inherited;
  //EnableSet('Ovf', cbMode.ItemIndex < 2);
  rbOvf.Enabled := cbMode.ItemIndex < 2;
  EnableSet('Time', cbMode.ItemIndex in [0..1]);
  cbUnitTime.Enabled := cbMode.ItemIndex in [0..1];
  EnableSet('Ticks', cbMode.ItemIndex in [0..1]);

  if (not rbOvf.Enabled) then begin
    if (rbOvf.Checked) then rbTicks.Checked := true;
    edOvf.Text := '';
  end;

  case cbMode.ItemIndex of
    0: SetLabels('Ovf', getLocaleString('overflows'), '');
    else SetLabels('Ovf', GetString('#global\frequency'), GetString('#global\hz'));
  end;

  Self.DoFieldsChange(nil);
end;

procedure TfmAVRTimerCalc.DoCalcTimer;
var t,k: single;
begin
  if rbOvf.Checked then begin
    t := CheckField(edOvf);
    if (t<0) then exit;

    t := t*sfTop[cbResolution.ItemIndex];
    edTicks.Text := Format('%.f', [t]);
    edTime.Text := Format('%.6f', [GetTickTime*t*sfUnit[cbUnitTime.ItemIndex]]);
  end else
  if rbTime.Checked then begin
    t := StrToFloatDef(edTime.Text,0)/sfUnit[cbUnitTime.ItemIndex];
    k := t/GetTickTime-1;
    if (k<=0) then k := 0;
    edTicks.Text := Format('%.2f', [k]);
    edOvf.Text := Format('%.f', [Int(GetOvfCount(k))]);
  end
  else begin
    t := StrToIntDef(edTicks.Text,0);
    edOvf.Text := Format('%.f', [Int(GetOvfCount(t))]);
    edTime.Text := Format('%.6f', [GetTickTime*t*sfUnit[cbUnitTime.ItemIndex]]);
  end;
end;

procedure TfmAVRTimerCalc.DoCalcCTC;
var t,k: single;
begin
  if rbTime.Checked then begin
    t := StrToFloatDef(edTime.Text,0)/sfUnit[cbUnitTime.ItemIndex];
    k := t/GetTickTime;
    if (k<=0) then k := 0;
    edTicks.Text := Format('%.2f', [k]);
    edOvf.Text := Format('%f', [1.0/t]);

    HighliteErrorField(edTicks, k > sfTop[cbResolution.ItemIndex]-1);
  end else
  if rbOvf.Checked then begin
    if not CheckFieldMore(edOvf, 0) then exit;
    t := 1.0/StrToFloat(edOvf.Text);
    k := t/GetTickTime;

    edTime.Text := Format('%.6f', [t*sfUnit[cbUnitTime.ItemIndex]]);
    edTicks.Text := Format('%.2f', [k]);

    HighliteErrorField(edTicks, k > sfTop[cbResolution.ItemIndex]-1);
  end
  else begin
    if not (CheckFieldRange(edTicks, 0, sfTop[cbResolution.ItemIndex], false)) then exit;

    k := StrToFloatDef(edTicks.Text,0);
    t := GetTickTime*k;
    if (t<=0) then exit;
    edOvf.Text := Format('%f', [1.0/t]);
    edTime.Text := Format('%.6f', [t*sfUnit[cbUnitTime.ItemIndex]]);
  end;
end;

procedure TfmAVRTimerCalc.DoCalcFPWM;
var t: single;
begin
  t := StrToFloat(cbFreq.Text)/(sfPresc[cbPrescaler.ItemIndex]*sfTop[cbResolution.ItemIndex]);
  edOvf.Text := Format('%.f', [t]);
end;

procedure TfmAVRTimerCalc.DoCalcPPWM;
var t: single;
begin
  t := StrToFloat(cbFreq.Text)/(2*sfPresc[cbPrescaler.ItemIndex]*sfTop[cbResolution.ItemIndex]);
  edOvf.Text := Format('%.f', [t]);
end;

initialization
  RegisterCalc(TfmAVRTimerCalc);

end.
